package com.gitee.chili.leisure.listener;

import com.gitee.chili.leisure.core.*;
import com.gitee.chili.leisure.model.MessageCmdEnum;
import io.netty.channel.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Map;


/**
 * 服务启动时加载的钩子
 * <p>
 * 在服务启动时，这里注册了所有的 {@code netty} 处理器，如果你没有配备其他的处理器的情况下，将采用默认的注册器，这些处理器将通过
 * {@link MessageHandlerRegistry#register(MessageCmdEnum.Command, MessageHandler)} 注册到消息处理器缓存中，这里存储了你所有
 * 的处理器实例。
 * <p>
 * 如何使用处理器处理 {@code proto} 消息，默认通过 {@link DefaultMessageReceiver#onReceive(Packet, Channel)}接收器来接收你的
 * 消息。根据指定的命令，通过 {@link DefaultMessageReceiver#getRegistry()} 消息注册器获取到处理器，在处理器中会完成编码把对应的
 * 二进制数据转换为可用的消息，通过反射机制，实例化消息，最后执行处理方法。
 * <p>
 * 能被扫描到的前置条件
 * 被{@link NettyHandler} 标记的类将成为一个处理器类。
 * 被标记的类中的方法必须拥有 {@link Handler} 标识代表这是一个处理器的处理消息方法
 *
 * @author week solider
 * @since 2020-07-29
 */

@Slf4j
@Component
public class NettyHandlerRegisterListener implements ApplicationListener<ContextRefreshedEvent> {

    /**
     * 处理应用程序的事件
     *
     * @param event 事件响应
     */
    @SuppressWarnings("unchecked")
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {

        log.info("NettyHandlerRegisterListener initializing");
        ApplicationContext ctx = event.getApplicationContext();

        log.debug("===============================================");
        log.debug("Start registering netty handler");

        Map<String, Object> beans = ctx.getBeansWithAnnotation(NettyHandler.class);

        for (Object obj : beans.values()) {

            Class<?> clazz = obj.getClass();
            log.debug("Scan to {} , Registering handler", clazz.getName());

            for (Method method : clazz.getDeclaredMethods()) {
                Handler handle = method.getAnnotation(Handler.class);
                if (handle != null) {

                    method.setAccessible(true);
                    ReceiverType[] receivers = handle.receivers();
                    Class<?>[] paramTypes = method.getParameterTypes();
                    Class<? extends AbstractDefaultMessage> paramClass = (Class<? extends AbstractDefaultMessage>) paramTypes[0];

                    log.debug("The loading handle , method: {}  , cmd: {} , param type: {} , role: {} ",
                             method.getName(), handle.cmd(), paramClass.getName(), handle.desc());

                    for (ReceiverType receiver : receivers) {

                        if (paramTypes.length != 1) {
                            log.error("Packet handler method parameter count violation. Wrong parameter number: {}, the correct number is 1.", paramTypes.length);
                            return;
                        }

                        // 方法只运行拥有一个参数 并且是一个 proto 的消息类型
                        if (!paramTypes[0].getSuperclass().equals(AbstractDefaultMessage.class)) {
                            log.error("The parameter to the packet handler method must be {}", AbstractDefaultMessage.class.getName());
                            return;
                        }

                        // 消息处理器
                        DefaultMessageHandler<AbstractDefaultMessage> messageBaseHandler = new DefaultMessageHandler<>(obj, method, paramClass, handle.isAuthentication());

                        DefaultMessageReceiver messageReceiver = receiver.getReceiver();

                        MessageHandlerRegistry registry = messageReceiver.getRegistry();

                        // 注册处理器
                        registry.register(handle.cmd(), messageBaseHandler);
                    }
                }
            }

            log.debug("netty handler register finish");
            log.debug("===============================================");

        }

    }
}
